package chantask

import (
	"gitee.com/ymofen/gobase"
	"sort"
	"sync"
	"sync/atomic"
)

type ChanTaskHub struct {
	lk  sync.RWMutex
	lst map[string]*ChanTask
}

var (
	DefaultChanTaskHub *ChanTaskHub
)

func NewChanTaskHub() *ChanTaskHub {
	rval := &ChanTaskHub{
		lst: make(map[string]*ChanTask),
	}
	return rval
}

func init() {
	DefaultChanTaskHub = NewChanTaskHub()
}

// false: 不存在, 或者有引用
// true: 停止成功
func (this *ChanTaskHub) ReleaseChanTask(id string) bool {
	var refcnt int32
	this.lk.RLock()
	task := this.lst[id]
	if task != nil {
		refcnt = atomic.AddInt32(&task.refcnt, -1)
	}
	this.lk.RUnlock()
	if task == nil {
		return false
	}
	if refcnt == 0 {
		this.lk.Lock()
		defer this.lk.Unlock()
		refcnt = atomic.LoadInt32(&task.refcnt)
		if refcnt == 0 {
			delete(this.lst, id)
			go task.Stop() // 避免task.正在执行任务中的复杂情况，导致死锁，使用异步关闭
			return true
		}
	}
	return false
}

func (this *ChanTaskHub) StatusDetails(all int) string {
	lst := make([][2]interface{}, 0)
	this.lk.RLock()
	for k, v := range this.lst {
		if v.Remain() > 0 || all == 1 {
			var itm [2]interface{}
			itm[0] = k
			itm[1] = v
			lst = append(lst, itm)
		}
	}
	this.lk.RUnlock()
	sort.Slice(lst, func(i, j int) bool {
		return lst[i][0].(string) < lst[j][0].(string)
	})
	var sb gobase.BytesBuilder
	for i := 0; i < len(lst); i++ {
		itm := lst[i]
		task := itm[1].(*ChanTask)
		cnt1, cnt2, refcnt := atomic.LoadInt32(&task.pushcnt), atomic.LoadInt32(&task.popcnt), atomic.LoadInt32(&task.refcnt)
		if sb.Len() > 0 {
			sb.AppendStr("\n")
		}
		sb.Appendf("%s(%d) %d-%d=%d", itm[0].(string), refcnt, cnt1, cnt2, cnt1-cnt2)
	}
	return sb.String()
}

func (this *ChanTaskHub) CheckGetChanTask(id string, maxcache int, priority int) (task *ChanTask, refcnt int32) {
	this.lk.RLock()
	task = this.lst[id]
	if task != nil {
		refcnt = atomic.AddInt32(&task.refcnt, 1)
	}
	this.lk.RUnlock()
	if task != nil {
		return
	}

	this.lk.Lock()
	defer this.lk.Unlock()
	task = this.lst[id]
	if task != nil {
		refcnt = atomic.AddInt32(&task.refcnt, 1)
		return
	}
	task = NewChanTask(maxcache)
	task.id = id
	refcnt = atomic.AddInt32(&task.refcnt, 1)
	this.lst[id] = task
	return
}

func CheckGet(id string, maxcache int, priority int) (task *ChanTask, refcnt int32) {
	return DefaultChanTaskHub.CheckGetChanTask(id, maxcache, priority)
}

func Release(id string) bool {
	return DefaultChanTaskHub.ReleaseChanTask(id)
}
